package com.wzcl.app.service.impl;

import com.wzcl.app.dao.model.*;
import com.wzcl.app.dao.repository.RequisitionRepository;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.model.common.MyPage;
import com.wzcl.app.model.common.WZCL;
import com.wzcl.app.model.dto.RequisitionDto;
import com.wzcl.app.service.GradeService;
import com.wzcl.app.service.RequisitionService;
import com.wzcl.app.service.SubjectService;
import com.wzcl.app.service.UserService;
import com.wzcl.app.utils.CollectionUtil;
import com.wzcl.app.utils.LockUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

@Log4j2
@Service
public class RequisitionServiceImpl implements RequisitionService {
    private static final String CREATE_KEY = "REQUISITION_CREATE_LOCK_";

    @Autowired
    private RequisitionRepository repository;

    @Autowired
    private UserService userService;

    @Autowired
    private SubjectService subjectService;

    @Autowired
    private GradeService gradeService;

    @Override
    public Requisition addRequisition(RequisitionDto requisitionDto) {
        AppUser user = userService.getAndCheckByPhone(requisitionDto.getUserPhone());
        if (LockUtil.isLocked(CREATE_KEY + user.getPhone())) {
            throw new BusinessException(CodeMsg.REQUISITION_CREATING);
        }
        LockUtil.lock(CREATE_KEY + user.getPhone(), 5 * 60);
        try {
            Requisition requisition = repository.findByGradeIdAndSubjectIdAndUserId(requisitionDto.getGradeId(), requisitionDto.getSubjectId(), user.getId());
            if (requisition != null) return requisition;
            //年级 和 学科
            Subject subject = subjectService.getAndCheckById(requisitionDto.getSubjectId());
            Grade grade = gradeService.getAndCheckById(requisitionDto.getGradeId());
            requisition = new Requisition();
            //课程
            requisition.setGradeId(grade.getId());
            requisition.setGradeName(grade.getName());
            //学科
            requisition.setSubjectId(subject.getId());
            requisition.setSubjectName(subject.getName());
            //用户
            requisition.setUserId(user.getId());
            requisition.setUserPhone(user.getPhone());
            requisition.setUserRealName(user.getRealName());
            //星标，进度
            requisition.setStar(false);
            requisition.setSchedule(WZCL.SCHEDULE.PROCESSING);
            return repository.save(requisition);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("报名失败:{} \r\n 堆栈信息:{}", e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.SYSTEM_ERR);
        } finally {
            LockUtil.unlock(CREATE_KEY + user.getPhone());
        }
    }

    @Override
    public Requisition starRequisitionById(Integer id) {
        //校验获得
        Requisition requisition = getAndCheckById(id);
        //标记
        requisition.setStar(!requisition.getStar());
        return repository.save(requisition);
    }

    @Override
    public Requisition getRequisitionById(Integer id) {
        return repository.findById(id).orElse(null);
    }

    @Override
    public Requisition getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少申请ID");
        }
        Requisition requisition = getRequisitionById(id);
        if (requisition == null) {
            throw new BusinessException(CodeMsg.REQUISITION_NOT_EXIST);
        }
        return requisition;
    }

    @Override
    public Requisition endRequisition(RequisitionDto requisitionDto) {
        if (StringUtils.isBlank(requisitionDto.getResult())) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少处理结果");
        }
        //校验获得
        Requisition requisition = getAndCheckById(requisitionDto);
        //完结申请
        requisition.setResult(requisitionDto.getResult());
        requisition.setSchedule(WZCL.SCHEDULE.END);
        return repository.save(requisition);
    }

    @Override
    public List<Requisition> getRequisitionList(RequisitionDto requisitionDto) {
        return repository.findAll(getSpecification(requisitionDto), requisitionDto.getSort());
    }

    @Override
    public MyPage<Requisition> getRequisitionPage(RequisitionDto requisitionDto) {
        List<Requisition> all = repository.findAll(getSpecification(requisitionDto));
        ArrayList<Requisition> list = new ArrayList<>(all);
        List<Requisition> listBySort = CollectionUtil.getListBySort(list, "getCreateTime", "desc");
        List<Requisition> listByPage = CollectionUtil.getListByPage(listBySort, requisitionDto.getPageNumber(), requisitionDto.getPageSize());
        MyPage<Requisition> myPage = new MyPage<>();
        myPage.setContent(listByPage);
        myPage.setTotalElements(list.size());
        return myPage;
    }

    private Specification<Requisition> getSpecification(RequisitionDto requisitionDto) {
        return new Specification<Requisition>() {
            @Override
            public Predicate toPredicate(Root<Requisition> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (requisitionDto.getSubjectId() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("subjectId"), requisitionDto.getSubjectId()));
                }
                if (requisitionDto.getGradeId() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("gradeId"), requisitionDto.getGradeId()));
                }
                if (requisitionDto.getStar() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("star"), requisitionDto.getStar()));
                }
                if (requisitionDto.getSchedule() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("schedule"), requisitionDto.getSchedule()));
                }
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
